package TeamShadowCry.Source.Project;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.HashMap;

import TeamShadowCry.Source.Project.Process.Codec;
import TeamShadowCry.Source.Project.Process.CodecList;
import TeamShadowCry.Source.Project.Process.ProcessInstance.ProcessInstance;

public class MainInstance extends Thread{
	private File dir;
	private File Ramdir;
	private ArrayList<ProcessInstance> processList = new ArrayList<ProcessInstance>();
	private HashMap<ProcessInstance, InputStream> code = new HashMap<ProcessInstance, InputStream>();
	private HashMap<ProcessInstance, BufferedReader> code2 = new HashMap<ProcessInstance, BufferedReader>();
	private HashMap<ProcessInstance, byte[]> Ram = new HashMap<ProcessInstance, byte[]>();
	private HashMap<ProcessInstance, byte[]> extRam = new HashMap<ProcessInstance, byte[]>();
	private byte[] all = new byte[(int) Math.pow(1024,2)*64];
	private CodecList defaultCodecs = new CodecList();
	private boolean isWorking = true;
	public MainInstance(File dir) throws Exception{
		if (!dir.exists()){
			throw new Exception("Ordner existiert nicht!");
		}
		if (!dir.isDirectory()){
			throw new Exception("Angegebener Ordner is kein Ordner!");
		}
		this.dir = dir;
		this.Ramdir = new File(dir+"/Ram/");
		if (!Ramdir.exists()){
			Ramdir.mkdir();
		}
		if (!Ramdir.isDirectory()){
			Ramdir.delete();
			Ramdir.mkdir();
		}
		if (new File(Ramdir+"/all.backup").exists()){
			InputStream fs = new FileInputStream(new File(Ramdir+"/all.backup"));
			fs.read(all);
			fs.close();
		}
	}
	public void stopIt(){
		final Thread backup = this;
		new Thread(){
			public void run(){
				isWorking = false;
				try {
					OutputStream fout = new FileOutputStream(new File(Ramdir+"/all.backup"));
					fout.write(all);
					fout.close();
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				try {
					sleep(1000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				if (backup.isAlive()){
					backup.destroy();
				}
			}
		}.start();
	}
	public void add(ProcessInstance pr) throws IOException{
		processList.add(pr);
		Ram.put(pr, new byte[1024]);
		Codec get = defaultCodecs.getCodec(pr.language());
		if (pr.hasExtern() && get!=null){
			if (get.UseLine()){
				code2.put(pr, new BufferedReader(new InputStreamReader(new FileInputStream(pr.codeFile()))));
			}else{
				code.put(pr, new FileInputStream(pr.codeFile()));
			}
		}else{
			if (get.UseLine()){
				code2.put(pr, new BufferedReader(new InputStreamReader(new ByteArrayInputStream(pr.code()))));
			}else{
				code.put(pr, new ByteArrayInputStream(pr.code()));
			}
		}
		if (new File(Ramdir+"/"+pr.processName()+".backup").exists()){
			byte[] exRam = new byte[1024*64];
			InputStream read = new FileInputStream(new File(Ramdir+"/"+pr.processName()+".backup"));
			read.read(exRam);
			read.close();
			extRam.put(pr, exRam);
		}else{
			extRam.put(pr, new byte[1024*64]);
		}
	}
	public void remove(ProcessInstance pr) throws IOException{
		processList.remove(pr);
		Ram.remove(pr);
		if (defaultCodecs.getCodec(pr.language()).UseLine()){
			code2.get(pr).close();
			code2.remove(pr);
		}else{
			code.get(pr).close();
			code.remove(pr);
		}
		OutputStream fs = new FileOutputStream(new File(Ramdir+"/"+pr.processName()+".backup"));
		fs.write(extRam.get(pr));
		fs.close();
	}
	public String[] Tasklist(){
		ArrayList<String> list = new ArrayList<String>();
		for (ProcessInstance pr : processList){
			list.add(pr.processName());
		}
		return list.toArray(new String[list.size()]);
	}
	public int Taskkill(String task) throws IOException{
		int cc=0;
		for (ProcessInstance pr : processList){
			if (pr.processName().equalsIgnoreCase(task)){
				remove(pr);
				cc--;
			}
		}
		return cc;
	}
	public HashMap<String, Integer> getRest(){
		HashMap<String, Integer> list = new HashMap<String,Integer>();
		for (ProcessInstance pr : processList){
			try{
				list.put(pr.processName(), code.get(pr).available());
			}catch(Exception err){
				err.printStackTrace();
			}
		}
		return list;
	}
	public HashMap<String, String> getLanguage(){
		HashMap<String, String> list = new HashMap<String, String>();
		for (ProcessInstance pr : processList){
			list.put(pr.processName(), pr.language());
		}
		return list;
	}
	public HashMap<String, Boolean> getExtern(){
		HashMap<String, Boolean> list = new HashMap<String, Boolean>();
		for (ProcessInstance pr : processList){
			list.put(pr.processName(), pr.hasExtern());
		}
		return list;
	}
	public byte[] readNext(ProcessInstance pr, int ReadSize) throws IOException{
		boolean useExtern = pr.hasExtern();
		Codec toCode = defaultCodecs.getCodec(pr.language());
		byte[] readed = new byte[ReadSize];
		int size = code.get(pr).read(readed);
		if (size>-1){
			byte[] right = new byte[size];
			System.arraycopy(readed, 0, right, 0, right.length);
			return right;
		}else{
			return new byte[0];
		}
	}
	public String readNext(ProcessInstance pr) throws IOException{
		return code2.get(pr).readLine();
	}
	public void run(){
		while(isWorking){
			ArrayList<ProcessInstance> toDel = new ArrayList<ProcessInstance>();
			for (ProcessInstance pr : processList){
				boolean useExtern = pr.hasExtern();
				Codec toCode = defaultCodecs.getCodec(pr.language());
				if (toCode!=null){
					try {
						int ans = -1;
						if (toCode.UseLine()){
							String ln = readNext(pr);
							if (ln!=null){
								ans = toCode.aktion(ln.getBytes(), Ram.get(pr), extRam.get(pr), all, code2.get(pr),this, pr);
							}else{
								toDel.add(pr);
							}
						}else{
							if (code.get(pr).available()==0){
								toDel.add(pr);
							}else{
								ans = toCode.aktion(readNext(pr,toCode.toReadSize()), Ram.get(pr), extRam.get(pr), all, code.get(pr),this, pr);
							}
						}
						if (ans==0){
							toDel.add(pr);
						}
					} catch (Exception e) {
						e.printStackTrace();
					}
				}else{
					toDel.add(pr);
				}
			}
			for (ProcessInstance pr : toDel){
				try {
					remove(pr);
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
			if (processList.size()==0){
				isWorking=false;
				stopIt();
			}
		}
	}
}
